const SwaggerParser = require("@apidevtools/swagger-parser");
const fs = require("fs-extra");
const path = require("path");
const axios = require("axios");
const { SWAGGER_URL, SWAGGER_AUTH_TOKEN } = require("./config/api");

/**
 * 从 Swagger 文档生成 API 文件
 * @param {Object} options - 选项
 * @param {boolean} options.typescript - 是否生成 TypeScript 代码
 * @returns {Promise<void>}
 */
async function generateApiFiles(options = { typescript: false }) {
  try {
    console.log("开始获取 Swagger 文档...");

    // 获取 Swagger URL 和认证令牌
    const swaggerUrl = process.env.SWAGGER_URL || SWAGGER_URL;
    const authToken = process.env.SWAGGER_AUTH_TOKEN || SWAGGER_AUTH_TOKEN;

    if (!swaggerUrl) {
      throw new Error("未指定 Swagger 文档 URL，请在 .env 文件中设置 SWAGGER_URL");
    }

    // 使用 axios 直接获取 Swagger 文档
    const response = await axios.get(swaggerUrl, {
      headers: {
        Authorization: `${authToken}`,
        Accept: "application/json",
      },
    });

    // 保存原始的 Swagger 文档
    const originalApiDoc = response.data;

    // 设置全局引用以供类型生成使用
    global.apiDoc = originalApiDoc;

    // 使用 SwaggerParser.parse 而不是 dereference
    const api = await SwaggerParser.parse(originalApiDoc);

    if (!api || !api.paths) {
      throw new Error("无效的 Swagger 文档格式");
    }

    // console.log(`发现 ${Object.keys(api.paths).length} 个接口`);

    // 创建 api 目录
    const apiDir = process.env.API_OUTPUT_DIR
      ? path.resolve(process.env.API_OUTPUT_DIR)
      : path.join(process.cwd(), "api");

    await fs.ensureDir(apiDir);

    // 按照 tag 分组接口
    const apiGroups = {};

    for (const [path, methods] of Object.entries(api.paths)) {
      for (const [method, operation] of Object.entries(methods)) {
        const tags = operation.tags || ["default"];

        tags.forEach((tag) => {
          // 解析 tag 来确定目录结构
          const [category, module] = parseTag(tag);

          if (!apiGroups[category]) {
            apiGroups[category] = {};
          }
          if (!apiGroups[category][module]) {
            apiGroups[category][module] = [];
          }

          apiGroups[category][module].push({
            path,
            method,
            operation,
          });
        });
      }
    }

    // 生成各个模块的 API 文件
    for (const [category, modules] of Object.entries(apiGroups)) {
      // 创建分类目录
      const categoryDir = path.join(apiDir, category);
      await fs.ensureDir(categoryDir);

      if (options.typescript) {
        // 生成 TypeScript 版本
        for (const [module, operations] of Object.entries(modules)) {
          const moduleDir = path.join(categoryDir, module);
          await fs.ensureDir(moduleDir);

          // 生成 types.ts
          const typesContent = generateTypesContent(operations);
          await fs.writeFile(path.join(moduleDir, "types.ts"), typesContent);

          // 生成 index.ts
          const apiContent = generateTypeScriptApiContent(module, operations);
          await fs.writeFile(path.join(moduleDir, "index.ts"), apiContent);
        }
      } else {
        // 生成 JavaScript 版本
        for (const [module, operations] of Object.entries(modules)) {
          const content = generateApiContent(module, operations);
          const fileName = `${module}.js`;
          await fs.writeFile(path.join(categoryDir, fileName), content);
        }
      }
    }

    return {
      apiDir,
      apiCount: Object.keys(api.paths).length,
      categoryCount: Object.keys(apiGroups).length,
    };
  } catch (error) {
    console.error("生成 API 文件失败：", error.message);
    if (error.response) {
      console.error("服务器响应状态码:", error.response.status);
    }
    throw error;
  }
}

// 解析 tag 来确定目录结构
function parseTag(tag) {
  // 例如 "管理后台 - 签到规则" => ["管理后台", "签到规则"]
  const parts = tag.split(" - ");
  if (parts.length === 2) {
    return [parts[0].replace(/\s/g, ""), parts[1].replace(/\s/g, "")];
  }
  return ["其他", tag.replace(/\s/g, "")];
}

function generateApiContent(tag, operations) {
  let content = `import request from '@/utils/request';\n\n`;

  operations.forEach(({ path, method, operation }) => {
    let paramName = "params";
    let requestMethod = method;

    if (operation.requestBody) {
      const schema = operation.requestBody.content["application/json"]?.schema;
      if (schema?.$ref) {
        paramName = "data";
      }
      if (operation.requestBody.content["multipart/form-data"]) {
        requestMethod = "upload";
        paramName = "file";
      }
    }

    // 检查是否有 exportReqVO 参数
    if (operation.parameters?.some((param) => param.name === "exportReqVO")) {
      requestMethod = "download";
    }

    if (
      operation.responses?.["200"]?.content?.["application/octet-stream"] ||
      operation.responses?.["200"]?.content?.["application/vnd.ms-excel"]
    ) {
      requestMethod = "download";
    }

    const functionName =
      operation.operationId || `${method}${path.replace(/\/{/g, "By").replace(/}/g, "").replace(/\//g, "_")}`;

    content += `
/**
 * ${operation.summary || ""}
 * ${operation.description || ""}
 */`;

    if (paramName === "data" || paramName === "file" || operation.parameters?.length > 0) {
      content += `
export const ${functionName} = (${paramName}) => {
  return request.${requestMethod}({
    url: \`${path}\`,
    ${paramName}
  })
}\n\n`;
    } else {
      content += `
export const ${functionName} = () => {
  return request.${requestMethod}({
    url: \`${path}\`
  })
}\n\n`;
    }
  });

  return content;
}

function generateTypeScriptApiContent(module, operations) {
  let content = `import request from '@/config/axios';\n`;
  content += `import type { ${getTypeImports(operations)} } from './types';\n\n`;

  operations.forEach(({ path, method, operation }) => {
    const { paramType, paramName } = getParameterInfo(operation);
    let requestMethod = method;

    // 检查是否有 exportReqVO 参数
    if (operation.parameters?.some((param) => param.name === "exportReqVO")) {
      requestMethod = "download";
    }

    const functionName =
      operation.operationId || `${method}${path.replace(/\/{/g, "By").replace(/}/g, "").replace(/\//g, "_")}`;

    content += `
/**
 * ${operation.summary || ""}
 * ${operation.description || ""}
 */`;

    if (paramType) {
      content += `
export const ${functionName} = (${paramName}: ${paramType}) => {
  return request.${requestMethod}({
    url: \`${path}\`,
    ${paramName}
  })
}\n`;
    } else {
      content += `
export const ${functionName} = () => {
  return request.${requestMethod}({
    url: \`${path}\`
  })
}\n`;
    }
  });

  return content;
}

function generateTypesContent(operations) {
  const types = new Set();
  let schemas = {};

  // 递归收集类型依赖
  function collectTypeDependencies(ref) {
    if (!ref) return;
    const typeName = ref.split("/").pop();
    if (types.has(typeName)) return;

    types.add(typeName);
    const schema = getSchemaFromRef(ref);
    schemas[typeName] = schema;

    // 收集属性中的类型依赖
    if (schema?.properties) {
      Object.values(schema.properties).forEach((prop) => {
        if (prop.$ref) {
          collectTypeDependencies(prop.$ref);
        }
        if (prop.items?.$ref) {
          collectTypeDependencies(prop.items.$ref);
        }
      });
    }
  }

  operations.forEach(({ operation }) => {
    // 收集请求体类型
    if (operation.requestBody?.content["application/json"]?.schema?.$ref) {
      collectTypeDependencies(operation.requestBody.content["application/json"].schema.$ref);
    }

    // 收集参数类型
    if (operation.parameters) {
      operation.parameters.forEach((param) => {
        if (param.schema?.$ref) {
          collectTypeDependencies(param.schema.$ref);
        }
      });
    }

    // 收集响应类型
    if (operation.responses?.["200"]?.content?.["*/*"]?.schema?.$ref) {
      collectTypeDependencies(operation.responses["200"].content["*/*"].schema.$ref);
    } else if (operation.responses?.["200"]?.content) {
      // 尝试其他可能的内容类型
      for (const contentType in operation.responses["200"].content) {
        const schema = operation.responses["200"].content[contentType].schema;
        if (schema?.$ref) {
          collectTypeDependencies(schema.$ref);
        }
      }
    }
  });

  // 生成类型定义
  let content = "";
  types.forEach((typeName) => {
    const schema = schemas[typeName];
    if (schema) {
      content += `export interface ${typeName} {\n`;
      Object.entries(schema.properties || {}).forEach(([key, prop]) => {
        const required = schema.required?.includes(key);
        const type = getPropertyType(prop);
        const description = prop.description ? ` // ${prop.description}` : "";
        content += `  ${key}${required ? "" : "?"}: ${type};${description}\n`;
      });
      content += `}\n\n`;
    }
  });

  return content;
}

function getSchemaFromRef(ref) {
  // 从 #/components/schemas/TypeName 提取 TypeName
  const typeName = ref.split("/").pop();
  // 从全局 API 文档中获取 schema 定义
  const schema = global.apiDoc.components?.schemas?.[typeName];
  if (!schema) {
    console.warn(`找不到类型定义: ${typeName}, ref: ${ref}`);
  }
  return schema;
}

function getPropertyType(prop) {
  if (prop.$ref) {
    return prop.$ref.split("/").pop();
  }

  switch (prop.type) {
    case "integer":
    case "number":
      return "number";
    case "string":
      if (prop.enum) {
        return prop.enum.map((e) => `'${e}'`).join(" | ");
      }
      return "string";
    case "boolean":
      return "boolean";
    case "array":
      const itemType = prop.items.$ref ? prop.items.$ref.split("/").pop() : getPropertyType(prop.items);
      return `${itemType}[]`;
    case "object":
      if (prop.additionalProperties) {
        const valueType = getPropertyType(prop.additionalProperties);
        return `Record<string, ${valueType}>`;
      }
      return "Record<string, any>";
    default:
      return "any";
  }
}

function getTypeImports(operations) {
  const types = new Set();

  operations.forEach(({ operation }) => {
    // 检查请求体类型
    if (operation.requestBody?.content["application/json"]?.schema?.$ref) {
      const typeName = operation.requestBody.content["application/json"].schema.$ref.split("/").pop();
      types.add(typeName);
    }

    // 检查参数类型
    if (operation.parameters) {
      operation.parameters.forEach((param) => {
        if (param.schema?.$ref) {
          const typeName = param.schema.$ref.split("/").pop();
          types.add(typeName);
        }
      });
    }

    // 检查响应类型
    if (operation.responses?.["200"]?.content?.["*/*"]?.schema?.$ref) {
      const typeName = operation.responses["200"].content["*/*"].schema.$ref.split("/").pop();
      types.add(typeName);
    }
  });

  const typesList = Array.from(types);
  return typesList.join(", ");
}

function getParameterInfo(operation) {
  let paramType = "";
  let paramName = "params";

  if (operation.requestBody) {
    const schema = operation.requestBody.content["application/json"]?.schema;
    if (schema?.$ref) {
      paramType = schema.$ref.split("/").pop();
      paramName = "data";
    } else {
      paramType = "any";
    }
  } else if (operation.parameters) {
    const queryParams = operation.parameters.filter((p) => p.in === "query");
    if (queryParams.length === 1) {
      paramType = getTypeFromSchema(queryParams[0].schema);
    } else if (queryParams.length > 1) {
      paramType = `{ ${queryParams
        .map((p) => `${p.name}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`)
        .join(", ")} }`;
    }
  }

  return { paramType, paramName };
}

// 辅助函数：从 schema 获取 TypeScript 类型
function getTypeFromSchema(schema) {
  if (!schema) return "any";

  switch (schema.type) {
    case "integer":
    case "number":
      return "number";
    case "string":
      return "string";
    case "boolean":
      return "boolean";
    case "array":
      const itemType = schema.items?.$ref ? schema.items.$ref.split("/").pop() : getTypeFromSchema(schema.items);
      return `${itemType}[]`;
    default:
      if (schema.$ref) {
        return schema.$ref.split("/").pop();
      }
      return "any";
  }
}

module.exports = generateApiFiles;
